#ifndef _QUORUM_HPP
#define _QUORUM_HPP 1

#include <vector>
#include <boost/shared_ptr.hpp>
#include "VerifiedMessageBase.hpp"

namespace BFT
{
	namespace messages
	{
		template<class T>
		class Quorum
		{
			
			public:
			
			Quorum(int _maxSize, int _targetSize, int _offsetBase);
			bool isComplete();
			
			/**
			returns true if the entry is successfully added to the current
			set.  returns false if the quorum must be reset to accomodate
			the entry
			**/
			bool addEntry(boost::shared_ptr<T> _rep);
			std::vector<boost::shared_ptr<T> > getEntries();
			bool containsEntry(int _i);
			boost::shared_ptr<T> getEntry();
			void clear();
			
			private:
			
			boost::shared_ptr<T> val;
			std::vector<boost::shared_ptr<T> > entries;
			int quorumSize;
			int indexBase;
			int count;
		};
		
		template<class T> 
		bool Quorum<T>::isComplete() 
		{	
//			fprintf(stderr, "inside quorum::iscomplete()\n");
			return count >= quorumSize;
		}
		
		template<class T> 
		std::vector<boost::shared_ptr<T> > Quorum<T>::getEntries() 
		{
			return entries;
		}
		
		template<class T> 
		bool Quorum<T>::containsEntry(int _i) 
		{
			return entries.at(_i).use_count() != 0;
		}
		
		template<class T> 
		boost::shared_ptr<T> Quorum<T>::getEntry() 
		{
			return val;
		}
		
		template<class T>
		Quorum<T>::Quorum(int _maxSize, int _targetSize, int _offsetBase)
		{
			entries.resize(_maxSize);
			quorumSize = _targetSize;
			indexBase = _offsetBase;
			count = 0;
		}


		//TODO: optimize by increasing count after a successfull add
		/*
		template<class T>
		bool Quorum<T>::isComplete()
		{
			int count = 0;
			for (int i=0; i< entries.size(); i++)
			{
				if ( entries.at(i).use_count()!=0)
					count++;
			}
			return count >= quorumSize;
		}
		*/

		template<class T>
		bool Quorum<T>::addEntry(boost::shared_ptr<T> _rep)
		{
		
			if (val.use_count() == 0 || val.get()->matches(_rep))
			{
				if (val.use_count() == 0 && _rep.get()->isValid())
				{
					val = _rep;
					if(_rep.get()->getTag() == CONFIRM_VIEW)
						fprintf(stderr, "Quorum template says: ****** set val ***** \n");
				}
				else
				{
					if (val.use_count() == 0)
						return false; //_rep is not valid
				}
				
				if (entries.at( _rep.get()->getSender() - indexBase).use_count() == 0)
					count++;
				entries.at( _rep.get()->getSender() - indexBase) = _rep;
				return true;
			}
			else
			{
				if (!val.get()->matches(_rep) && _rep.get()->getTag() == CONFIRM_VIEW)
					fprintf(stderr, "Quorum template says: ******* NO MATCH!!! *****");
				if(_rep.get()->isValid())
				{
					//throwing out the old ones
					clear();
					val = _rep;
					entries.at( _rep.get()->getSender() - indexBase) = _rep;
					count++;
					return false;
				}
				else
				{
					return false;
				}
			}	
		}

		template<class T>
		void Quorum<T>::clear()
		{
			for (unsigned int i=0; i<entries.size(); i++)
				entries.at(i).reset();
			val.reset();
			count = 0;
		}
		
	}
}

#endif
